#pragma once

#include "rigidbody.h"
#include <vector>
#include "collide_fine.h"

namespace physics {
	//////////////////////////////////////
	// ForceGenerator abstract base class
	//////////////////////////////////////
	class ForceGenerator
	{
	public:
		virtual void updateForce(RigidBody *body, real dt) = 0;
	};

	///////////////////////////////////////////
	// Gravity
	// 
	// Applies the effect of gravity to the object
	///////////////////////////////////////////
	class Gravity : public ForceGenerator
	{
		// holds the acceleration due to gravity
		Vector3 gravity;

	public:
		// creates the force generator
		Gravity(const Vector3 &gravity);

		// applies the force to the given body
		virtual void updateForce(RigidBody *body, real dt);
	};

	///////////////////////////////////////////
	// Drag
	//
	// creates a drag force on the given object
	///////////////////////////////////////////
	class Drag : public ForceGenerator
	{
	private:
		// holds the first-order coefficient ( k1 * v )
		real k1;

		// holds the second-order coefficient (k2 * v * v)
		real k2;

	public:
		// creates the FG and stores the data
		Drag(real k1, real k2);
		// applies the force to the object
		virtual void updateForce(RigidBody *object, real dt);
	};

	///////////////////////////////////////////
	// Explosion
	///////////////////////////////////////////
	// Applies a force on all nearby objects
	// based on their distance from the center
	// of the explosion
	///////////////////////////////////////////
	class Explosion : public ForceGenerator
	{
	private:
		// tracks how long the explosion has been in operation
		real timePassed;

		// the location of the explosion
		Vector3 detonation;

		// speed that the wave is travelling, whic is related
		// to the thickness based on the following relationship
		// thickness >= speed * minimum frame duration
		//real shockwaveSpeed;

		// max radius of the explosion
		real radius;

		// the shockwave applies its force over a range of distances
		// which controls thickness. Faster waves require larger 
		// thicknesses
		//real shockwaveThickness;

		// this is the force that is applied at the very center of the
		// concussion wave on an object that is stationary. Objects that
		// that are in front, or behind, the wavefront, or that are 
		// already moving outward, get proportionally less force. Objects
		// moving in toward the center get proportionally more force.
		real peakConcussionForce;

		// the length of time that the concussion wave is active.
		// As the wave nears this, the forces it applies are reduced.
		//real concussionDuration;

	public:
		/*Explosion(const Vector3 &detonation,
					const real &speed,
					const real &thickness,
					const real &peakForce,
					const real &duration);*/
		Explosion(const Vector3 &detonation,
					const real &radius,
					const real &peakForce);

		virtual void updateForce(RigidBody *object, real dt);
	};


	///////////////////////////////////////////
	// ForceRegistry
	///////////////////////////////////////////
	// keeps track of all of the forces being applied to
	// every object so we can iterate through them
	///////////////////////////////////////////
	class ForceRegistry
	{
	protected:
		struct ForceRegistration
		{
			RigidBody *object; // the object having a force applied to it
			ForceGenerator *fg; // the force generator being applied to the object
		};

		// for tidier code
		typedef std::vector<ForceRegistration> Registry;
		
		// STL vector object that keeps track of all force generators
		// and their respective objects
		Registry forceGenerators;
	public:
		// adds a FG to the list
		void add(RigidBody *object, ForceGenerator *fg);
		// removes a FG from the list
		void remove(RigidBody *object);
		// removes all FGs
		void clear();
		// tells all force generators to update their corresponding objects
		void updateForces(real dt);
	};

	///////////////////////////////////////////
	// GlobalForceRegistry
	//
	// Applies the given ForceGenerator to ALL objects
	// provided by a RigidBody vector
	///////////////////////////////////////////
	class GlobalForceRegistry
	{
	protected:
		typedef std::vector<physics::CollisionPrimitive*> Objects;
		Objects *pObjectList; // the list of objects to have the force generator applied to

		// for tidier code
		typedef std::vector<ForceGenerator*> GlobalRegistry;
		
		// STL vector object that keeps track of all force generators
		// and their respective objects
		GlobalRegistry forceGenerators;
	public:
		// adds a FG to the list
		void add(ForceGenerator *fg);
		// removes a FG from the list
		void remove(ForceGenerator *fg);
		// sets the list of object
		void setObjectList(Objects* objects) {pObjectList = objects;};
		// removes all FGs
		void clear();
		// tells all force generators to update their corresponding objects
		void updateForces(real dt);
	};
}